-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Format Library: Assign Popover anchorRect to update selection position #14938
Conversation
Avoids need to explicitly support onClickOutside as deprecated pass-through prop, instead leveraging pass-through nature of spread props.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
position = 'bottom center', | ||
focusOnMount = 'firstElement', | ||
...popoverProps |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I reviewed it already earlier this week 😃
@gziolo Good catch. It's not reliable to continuously update the caret positioning when link interactions (add, edit) will place selection into the editing popover. @ellatrix I've pushed some updates which converge a bit more with your #14921, but still opts to render |
Yes, it's an edge case. I think it could work reliably though, you'd always end up with the right element. |
@@ -123,6 +123,20 @@ Opt-in prop to show popovers fullscreen on mobile, pass `false` in this prop to | |||
- Required: No | |||
- Default: `false` | |||
|
|||
### getAnchorRect |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we certain about the future of getAnchorRect
before documenting it? Perhaps we could work towards only supporting anchorRect
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we certain about the future of
getAnchorRect
before documenting it? Perhaps we could work towards only supportinganchorRect
.
I don't mind to remove the documentation, if even just to keep the changes well-contained. I don't have much a problem with this prop, at least in that it's a useful way to also base the computed anchor rect relative the passed default anchor
argument. I'm also not entirely comfortable with the prospect of removing functionality based on the absence of it from documentation either.
} | ||
|
||
const hasAnchorRect = this.props.hasOwnProperty( 'anchorRect' ); | ||
if ( hasAnchorRect !== prevProps.hasOwnProperty( 'anchorRect' ) ) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use hasOwnProperty
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use
hasOwnProperty
?
Because we expect the value could change over time, but scheduling the interval doesn't depend on the value, it just depends on whether a value exists.
i.e. if the alternative is this.props.anchorRect !== prevProps.anchorRect
, it would have unintended consequences on the auto-refresh.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works really well in my testing!
@@ -77,6 +77,39 @@ const LinkViewerUrl = ( { url } ) => { | |||
); | |||
}; | |||
|
|||
const URLPopoverAtLink = ( { isActive, addingLink, value, ...props } ) => { | |||
const anchorRect = useMemo( () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering... Does this have a limit on cache size? Sounds like it should have a limit of exactly one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering... Does this have a limit on cache size? Sounds like it should have a limit of exactly one.
It's not entirely clear, but it seems like something they would have considered as being the default behavior, but I also recall some similar discussion with @gziolo recently where he was showing a separate project which, by its name, might imply a different behavior 🤷♂️
https://reactjs.org/docs/hooks-reference.html#usememo
You may rely on useMemo as a performance optimization, not as a semantic guarantee. In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.
https://github.com/alexreardon/use-memo-one#readme
useMemo and useCallback cache the most recent result. However, this cache can be destroyed by React when it wants to:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, to me it sounded like it was not the intention of useMemo
to limit the cache to one by default. So I would consider swapping it. I'm generally wary of things that create cache without any limit. We're just taking more and more space from memory for no reason.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, to me it sounded like it was not the intention of
useMemo
to limit the cache to one by default. So I would consider swapping it. I'm generally wary of things that create cache without any limit. We're just taking more and more space from memory for no reason.
Oh, I was thinking the opposite by my interpretation; that yes, it would only store the one latest result in memory. I'll plan to double-check, though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I can discern, it only maintains a single value.
Feedback addressed, confirmed via DM to dismiss
WordPress#14938) * Block Editor: Pass spread props from URLPopover to Popover Avoids need to explicitly support onClickOutside as deprecated pass-through prop, instead leveraging pass-through nature of spread props. * Format Library: Assign Popover getAnchorRect to update selection position * Format Library: Compute memoized anchor from caret point or active element * Components: Add anchorRect prop to Popover component * Format Library: Provide direct reference to Popover anchorRect * Format Library: Consider next element sibling for initial caret placement Co-Authored-By: Ella van Durpe <[email protected]> * Components: Mark PositionedAtSelection as unstable * Components: Remove getAnchorRect mention from Popover README
#14938) * Block Editor: Pass spread props from URLPopover to Popover Avoids need to explicitly support onClickOutside as deprecated pass-through prop, instead leveraging pass-through nature of spread props. * Format Library: Assign Popover getAnchorRect to update selection position * Format Library: Compute memoized anchor from caret point or active element * Components: Add anchorRect prop to Popover component * Format Library: Provide direct reference to Popover anchorRect * Format Library: Consider next element sibling for initial caret placement Co-Authored-By: Ella van Durpe <[email protected]> * Components: Mark PositionedAtSelection as unstable * Components: Remove getAnchorRect mention from Popover README
#14938) * Block Editor: Pass spread props from URLPopover to Popover Avoids need to explicitly support onClickOutside as deprecated pass-through prop, instead leveraging pass-through nature of spread props. * Format Library: Assign Popover getAnchorRect to update selection position * Format Library: Compute memoized anchor from caret point or active element * Components: Add anchorRect prop to Popover component * Format Library: Provide direct reference to Popover anchorRect * Format Library: Consider next element sibling for initial caret placement Co-Authored-By: Ella van Durpe <[email protected]> * Components: Mark PositionedAtSelection as unstable * Components: Remove getAnchorRect mention from Popover README
if ( closest ) { | ||
return closest.getBoundingClientRect(); | ||
} | ||
}, [ isActive, addingLink, value.start, value.end ] ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know that isActive
needed to be listed as a dependency. It's not ever used in the useMemo
callback.
Fixes #14872
Fixes #11092
Alternative to #14921
Cherry-picks fc1fec7 from #14851
This pull request seeks to resolve an issue where repeated mounting of the link URLPopover component would cause excessive animations to occur in response to changes of selection or content within the formatted link. In doing so, it updates the behavior of link Popover to anchor to the link element itself, rather than follow the position of the caret.
Implementation Notes:
This reimplements the positioning to behave similar to
Autocomplete
in passinggetAnchorRect
to the rendered Popover, computed from the current selection. This replaces use ofPositionedAtSelection
.#14872 is partly caused by the fact that to update
PositionedAtSelection
, we must assign a newkey
value, which would unmount and remount the component, causing the animation to restart.It may be simpler to review using GitHub's "Hide whitespace changes" diff options, given the changes in
inline.js
are largely indentation changes.Testing instructions:
Repeat Steps to Reproduce from #14872, verifying that the animation only occurs once when within the link formatting.